package jp.co.nes.awssupport.compile.function;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.amazonaws.services.stepfunctions.model.StartExecutionResult;

import jp.co.nes.awssupport.business.bean.CommonContainerBean;
import jp.co.nes.awssupport.business.bean.CommonContainerBean.ProcessingResult;
import jp.co.nes.awssupport.business.bean.CommonInfoBean;
import jp.co.nes.awssupport.business.bean.ProcessInfoBean;
import jp.co.nes.awssupport.business.constant.OtherKeyForTargetYearMonth;
import jp.co.nes.awssupport.common.base.AbstractRequestHandler;
import jp.co.nes.awssupport.common.base.CommonException;
import jp.co.nes.awssupport.common.utility.AppInfoUtil;
import jp.co.nes.awssupport.common.utility.AwsStepFunctionsAsyncHelper;
import jp.co.nes.awssupport.common.utility.BeanUtil;
import jp.co.nes.awssupport.common.utility.DateUtil;

/**
 * 集計開始です(Lambda)。
 */
public class CompileStart extends AbstractRequestHandler<CommonInfoBean, CommonContainerBean<CommonInfoBean>> {

    /** ロガー */
    private static Logger logger = LoggerFactory.getLogger(CompileStart.class);

    /**
     * デフォルトコンストラクタです。
     */
    public CompileStart() {
    }

    /*
     * (非 Javadoc)
     * @see jp.co.nes.awssupport.common.base.AbstractRequestHandler#handleRequest(java.lang.Object)
     */
    @Override
    protected CommonContainerBean<CommonInfoBean> handleRequest(CommonInfoBean input) {
        String category = "CompileStart";
        String destinationOfBefore = AppInfoUtil.getValAndEmptyChk(category, "DestinationOfCompileProcessBefore");
        String destinationOfControl = AppInfoUtil.getValAndEmptyChk(category, "DestinationOfProcessControl");
        String processNameOfControl = AppInfoUtil.getValAndEmptyChk(category, "ProcessNameOfProcessControl");
        int appInfoNumberOfControl = AppInfoUtil.getIntValAndEmptyChk(category, "AppInfoNumberOfProcessControl");
        int startWaitTimeOfControl = AppInfoUtil.getIntValAndEmptyChk(category, "StartWaitTimeOfProcessControl");
        String destinationOfAfter = AppInfoUtil.getValAndEmptyChk(category, "DestinationOfCompileProcessAfter");
        int startWaitTimeOfAfter = AppInfoUtil.getIntValAndEmptyChk(category, "StartWaitTimeOfCompileProcessAfter");

        if (logger.isDebugEnabled()) {
            logger.debug(category + ",DestinationOfCompileProcessBefore=" + destinationOfBefore);
            logger.debug(category + ",DestinationOfProcessControl=" + destinationOfControl);
            logger.debug(category + ",ProcessNameOfProcessControl=" + processNameOfControl);
            logger.debug(category + ",AppInfoNumberOfProcessControl=" + appInfoNumberOfControl);
            logger.debug(category + ",StartWaitTimeOfProcessControl=" + startWaitTimeOfControl);
            logger.debug(category + ",DestinationOfCompileProcessAfter=" + destinationOfAfter);
            logger.debug(category + ",StartWaitTimeOfCompileProcessAfter=" + startWaitTimeOfAfter);
        }

        String preName = DateUtil.toString(DateUtil.getNowOfZonedDateTime());

        Map<String, String> other = input.getOther();
        List<String> targetYearMonthValueList = new ArrayList<>();
        OtherKeyForTargetYearMonth[] keys = OtherKeyForTargetYearMonth.values();
        for (OtherKeyForTargetYearMonth key : keys) {
            String targetYearMonthValue = other.get(key.getMapKey());
            if (StringUtils.isEmpty(targetYearMonthValue)) {
                if (logger.isDebugEnabled()) {
                    logger.debug("targetYearMonthValue is null or empty(key=" + key + ")");
                }
                continue;
            }

            if (logger.isDebugEnabled()) {
                if (logger.isDebugEnabled()) {
                    logger.debug("targetYearMonthValue is exist(key=" + key + ")");
                }
            }

            targetYearMonthValueList.add(targetYearMonthValue);
        }

        // 集計前処理を開始
        for (String targetYearMonthValue : targetYearMonthValueList) {
            CommonInfoBean infoOfBefore = new CommonInfoBean();
            infoOfBefore.setRunningMode(input.getRunningMode());
            infoOfBefore.setTargetYearMonthValue(targetYearMonthValue);
            CommonContainerBean<CommonInfoBean> containerOfBefore = new CommonContainerBean<>();
            containerOfBefore.setInfoBean(infoOfBefore);
            startExecutionAsync(destinationOfBefore, preName, containerOfBefore);
        }

        // 処理管理を開始
        ProcessInfoBean infoOfControl = new ProcessInfoBean();
        infoOfControl.setRunningMode(input.getRunningMode());
        infoOfControl.setProcessName(processNameOfControl);
        infoOfControl.setAppInfoNumber(appInfoNumberOfControl);
        infoOfControl.setWaitTime(startWaitTimeOfControl);
        CommonContainerBean<ProcessInfoBean> containerOfControl = new CommonContainerBean<>();
        containerOfControl.setInfoBean(infoOfControl);
        startExecutionAsync(destinationOfControl, preName, containerOfControl);

        // 集計後処理を開始
        for (String targetYearMonthValue : targetYearMonthValueList) {
            CommonInfoBean infoOfAfter = new CommonInfoBean();
            infoOfAfter.setRunningMode(input.getRunningMode());
            infoOfAfter.setTargetYearMonthValue(targetYearMonthValue);
            infoOfAfter.setWaitTime(startWaitTimeOfAfter);
            String[] mapKeys = new String[] { "sforceUpdateExec", "sforceUpdateSuccessDataCleanExec",
                    "sforceUpdateLinkedAccountIds" };
            Map<String, String> otherOfAfter = new HashMap<>();
            for (String key : mapKeys) {
                otherOfAfter.put(key, other.get(key));
            }
            infoOfAfter.setOther(otherOfAfter);
            CommonContainerBean<CommonInfoBean> containerOfAfter = new CommonContainerBean<>();
            containerOfAfter.setInfoBean(infoOfAfter);
            startExecutionAsync(destinationOfAfter, preName, containerOfAfter);

            // 集計後処理は600秒ずらして起動するようにする(Salesforceへのアクセスが同時に発生しないようにするため)
            startWaitTimeOfAfter = startWaitTimeOfAfter + 600;
        }

        // 返却内容の設定
        CommonInfoBean info = new CommonInfoBean();
        BeanUtil.copyProperties(info, input);
        CommonContainerBean<CommonInfoBean> output = new CommonContainerBean<>();
        output.setInfoBean(info);
        output.setProcessingResult(ProcessingResult.SUCCESS);
        return output;
    }

    /**
     * ステートマシンを非同期で実行します。
     *
     * @param destination 実行するステートマシンのarn
     * @param preName 実行名のプリフィックス
     * @param input 入力内容
     */
    private void startExecutionAsync(String destination, String preName, CommonContainerBean<?> input) {
        AwsStepFunctionsAsyncHelper helper = null;
        try {
            helper = createAwsStepFunctionsAsyncHelper();
            Future<StartExecutionResult> future = helper.startExecutionAsync(destination, preName, input);
            StartExecutionResult startExecutionResult = future.get();

            if (logger.isDebugEnabled()) {
                StringBuilder builder = new StringBuilder();
                builder.append("StartExecutionResult=");
                builder.append(startExecutionResult.getStartDate());
                builder.append(": ");
                builder.append(startExecutionResult.getExecutionArn());
                logger.debug(builder.toString());
            }
        } catch (InterruptedException | ExecutionException e) {
            throw new CommonException(e);
        } finally {
            if (helper != null) {
                helper.shutdown();
            }
        }
    }

    /**
     * AwsStepFunctionsAsyncヘルパーを生成します。
     *
     * @return AwsStepFunctionsAsyncヘルパー
     */
    protected AwsStepFunctionsAsyncHelper createAwsStepFunctionsAsyncHelper() {
        return new AwsStepFunctionsAsyncHelper();
    }
}
